
#include <iostream>
#include <list>
#include <string>

using namespace std;
/*	观察者模式
* 代码顺序很重要， 原先是不同的.h / .cpp 
* 现在放一个文件中之后，必须先把.h的内容全部拷贝过来之后，后面再跟.cpp的内容。否则就会报错
* error: invalid use of incomplete type 'class Observer'
* class Observer;
* #   note: forward declaration of 'class Observer'
*/

/***************		subject.h		*************/

class Observer;
class Subject {
public:
    virtual void SetState(const string& st) = 0;
    virtual string GetState() = 0;
    virtual void Attach(Observer* obv);
    virtual void Detach(Observer* obv);
    virtual void Notify();
    virtual ~Subject() = default;
protected:
    Subject() = default;
private:
    list<Observer*> _obvs;
};

class ConcreteSubject :public Subject {
public:
    string GetState();

    void SetState(const string& st);
private:
    string _st;
};


/*******************		Observer.h	***************/

class Observer {
public:
    virtual void Update(Subject* sub) = 0;
    virtual void PrintInfo() = 0;
    virtual ~Observer() = default;
protected:
    Observer() = default;
    string _st;
};

class ConcreteObserver :public Observer {
public:
    virtual Subject* GetSubject();

    ConcreteObserver(Subject* sub);

    ~ConcreteObserver();

    void Update(Subject* sub);

    void PrintInfo();

private:
    Subject *_sub;
};


/***************		subject.cpp		*************/
void Subject::Attach(Observer* obv) {
    _obvs.push_front(obv);
}

void Subject::Detach(Observer* obv) {
    if (obv != nullptr) {
        _obvs.remove(obv);
    }
}

void Subject::Notify() {
    for (auto obv : _obvs) {
        obv->Update(this);
    }
}

string ConcreteSubject::GetState() {
    return _st;
}

void ConcreteSubject::SetState(const string& st) {
    _st = st;
}

/***************************	Observer.cpp	****************************/


Subject* ConcreteObserver::GetSubject() {
    return _sub;
}

ConcreteObserver::ConcreteObserver(Subject* sub) {
    _sub = sub;
    _sub->Attach(this);
}

ConcreteObserver::~ConcreteObserver() {
    _sub->Detach(this);
    //if (_sub != nullptr) {
    //    delete _sub;
    //}
}

void ConcreteObserver::Update(Subject* sub) {
    _st = sub->GetState();
    PrintInfo();
}

void ConcreteObserver::PrintInfo() {
    cout << "ConcreteObserver::PrintInfo\t" << _sub->GetState() << endl;
}



int main() {
    Subject *sub = new ConcreteSubject();
    Observer *o1 = new ConcreteObserver(sub);
    Observer *o2 = new ConcreteObserver(sub);

    sub->SetState("old");
    sub->Notify();
    sub->SetState("new");
    sub->Notify();

    delete o1;
    delete o2;
    delete sub;

    return 0;
}
